package nf.graph.persistence;

import java.io.*;
import java.util.Scanner;
import java.util.regex.*;

class Parser
{
	public LineNumberReader reader;
	public String curLine;
	public int curLineNumber;
	public int linePos;
	
	private static final Pattern WHITESPACE = Pattern.compile("^\\s+");

	public Parser(File file) throws FileNotFoundException
	{
		this(new FileReader(file));
	}

	public Parser(String fileContents)
	{
		this(new StringReader(fileContents));
	}

	public Parser(Reader reader)
	{
		this.reader = new LineNumberReader(reader);
		curLine = null;
		curLineNumber = 0;
	}
	
	public void debugLine()
	{
		System.err.printf("[%d]: %s|%s%n", curLineNumber,
			curLine.substring(0, linePos), curLine.substring(linePos));
	}
	
	public int getLineNumber()
	{
		return curLineNumber;
	}

	public String expect(String pattern)
	{
		return expect(Pattern.compile(pattern));
	}
	
	public String expect(Pattern pattern)
	{
		return expect(pattern, true);
	}
	
	public String expect(String pattern, boolean advancePossible)
	{
		return expect(Pattern.compile(pattern), advancePossible);
	}

	public String expect(Pattern pattern, boolean advancePossible)
	{
		Matcher m = match(pattern, advancePossible);
		
		if (m == null) {
			throw new ParseException("Expected \'" + pattern.pattern() +
				"\'.", curLineNumber);
		}
		
		linePos = m.end();
		return m.group();
	}
	
	public String get(String pattern)
	{
		return get(Pattern.compile(pattern));
	}
	
	public String get(Pattern pattern)
	{
		return get(pattern, true);
	}
	
	public String get(String pattern, boolean advancePossible)
	{
		return get(Pattern.compile(pattern), advancePossible);
	}
	
	public String get(Pattern pattern, boolean advancePossible)
	{
		Matcher m = match(pattern, advancePossible);
		if (m == null)
			return null;
		
		linePos = m.end();
		return m.group();
	}
	
	public String peek(String pattern)
	{
		return peek(Pattern.compile(pattern));
	}
	
	public String peek(Pattern pattern)
	{
		return peek(pattern, true);
	}
	
	public String peek(String pattern, boolean advancePossible)
	{
		return peek(Pattern.compile(pattern), advancePossible);
	}
	
	public String peek(Pattern pattern, boolean advancePossible)
	{
		try {
			Matcher m = match(pattern, advancePossible);
			return (m != null)
				? m.group()
				: null;
		} catch (ParseException e) {
			 if (e.getCause() instanceof EOFException) {
				return null;
			}
			throw e;
		}
	}
	
	public boolean skip(String pattern)
	{
		return skip(Pattern.compile(pattern));
	}
	
	public boolean skip(Pattern pattern)
	{
		try {
			Matcher m = match(pattern, true);
			if (m == null)
				return false;
			linePos = m.end();
			return true;
		} catch (ParseException e) {
			if (e.getCause() instanceof EOFException) {
				return false;
			}
			throw e;
		}
	}

	public String nextLine()
	{
		return null;
	}
	
	public Matcher match(Pattern pattern)
	{
		return match(pattern, true);
	}
	
	public Matcher match(Pattern pattern, boolean canAdvance)
	{
		Matcher m = null;
		
		do {
			boolean lineEnd = (curLine == null || linePos == curLine.length());

			if (canAdvance && lineEnd) {
				advanceLine(pattern);
			} else if (curLine == null) {
				throw new ParseException("No line available to search in for '" +
					pattern.pattern() + "'.", curLineNumber);
			}
			
			m = WHITESPACE.matcher(curLine);
			m.region(linePos, curLine.length());
			if (m.find()) {
				linePos += m.group().length();
			}

			m = pattern.matcher(curLine);
			if (!m.find(linePos) || m.start() != linePos) {
				if (linePos < curLine.length()) {
					return null;
				}
			} else {
				return m;
			}
		} while (canAdvance);
		
		
		return null;
	}
	
	private void advanceLine(Pattern pattern)
	{
		try {
			getNextLine();
		} catch (EOFException e) {
			throw new ParseException("Unexpected end of file while " +
				"looking for '" + pattern.pattern() + "'.",
				curLineNumber, e);
		} catch (IOException e) {
			throw new ParseException("I/O error while looking for " +
				pattern.pattern() + "'.", curLineNumber, e);
		}
	}

	private void getNextLine() throws IOException
	{
		curLine = reader.readLine();
		if (curLine == null) {
			throw new EOFException();
		}

		curLineNumber = reader.getLineNumber();
		linePos = 0;
	}
}
